#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <stdlib.h>

#define MAXSEM 5
    
int fullid;
int emptyid;
int mutexid;
  
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
    struct seminfo *_buf;
};

int main()
{

    struct sembuf P, V;
    union semun arg;

    // 定义四个共享内存的ID
    int arrayid;
    int setid;
    int getid;
    int sumid;

    // 定义共享内存虚拟地址
    int *array;
    int *set;
    int *get;
    int *sum;

    // 创建共享内存
    arrayid = shmget(IPC_PRIVATE, sizeof(int) * MAXSEM, IPC_CREAT | 0666);
    setid = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666);
    getid = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666);
    sumid = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666);

    // 初始化共享内存
    array = (int *)shmat(arrayid, 0, 0);
    get = (int *)shmat(getid, 0, 0);
    *get = 0;
    set = (int *)shmat(setid, 0, 0);
    *set = 0;
    sum = (int *)shmat(sumid, 0, 0);
    *sum = 0;

    // 创建信号量
    fullid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    emptyid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    mutexid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);

    // 初始化信号量
    arg.val = 0;
    if (semctl(fullid, 0, SETVAL, arg) == -1)
        perror("semctl setval error");
    arg.val = MAXSEM;
    if (semctl(emptyid, 0, SETVAL, arg) == -1)
        perror("semctl setval error");
    arg.val = 1;
    if (semctl(mutexid, 0, SETVAL, arg) == -1)
        perror("setctl setval error");
    // 初始化PV操作
    P.sem_num = 0;
    P.sem_op = -1;
    P.sem_flg = SEM_UNDO;
    V.sem_num = 0;
    V.sem_op = 1;
    V.sem_flg = SEM_UNDO;
    // 消费者A进程
    if (fork() == 0)
    {
        while (1)
        {
            if (*get == 10)
                break;
            semop(fullid, &P, 1);
            semop(mutexid, &P, 1);
            printf("The ComsumerA get number from NO. %d\n", (*get) % MAXSEM);
            (*sum) = (*sum) + array[(*get) % MAXSEM];
            (*get)++;
            semop(mutexid, &V, 1);
            semop(emptyid, &V, 1);
            sleep(1);
        }
        printf("ConsumerA is over\n");
        exit(0);
    }
    else
    {   
        //消费者B进程
        if (fork() == 0)
        {
            while (1)
            {
                if (*get == 10)
                    break;
                semop(fullid, &P, 1);
                semop(mutexid, &P, 1);
                printf("The ComsumerB get number from NO. %d\n", (*get) % MAXSEM);
                (*sum) = (*sum) + array[(*get) % MAXSEM];
                (*get)++;
                semop(mutexid, &V, 1);
                semop(emptyid, &V, 1);
                sleep(1);
            }
            printf("ConsumerB is over\n");
            exit(0);
        }
        else
        {
            // 生产者进程
            if (fork() == 0)
            {
                int i = 0;
                while (i < 10)
                {
                    semop(emptyid, &P, 1);
                    semop(mutexid, &P, 1);
                    array[*(set) % MAXSEM] = i + 1;
                    printf("Producer put number %d to NO. %d\n", array[(*set) % MAXSEM],
                           (*set) % MAXSEM);
                    (*set)++;
                    semop(mutexid, &V, 1);
                    semop(fullid, &V, 1);
                    i++;
                }
                sleep(5);
                printf("Producer is over\n");
                exit(0);
            }
        }
    }
    // 父进程回收子进程
    wait(0);
    wait(0);
    wait(0);
    // 父进程输出信息总和sum
    printf("The ComsumerA&B got numbers from shared buffer,the total is: %d\n", (*sum));
    // 断开并撤销共享内存
    shmdt(array);
    shmctl(arrayid, IPC_RMID, 0);
    shmdt(get);
    shmctl(getid, IPC_RMID, 0);
    shmdt(set);
    // 撤销信号量
    shmctl(setid, IPC_RMID, 0);
    semctl(emptyid, IPC_RMID, 0);
    semctl(fullid, IPC_RMID, 0);
    semctl(mutexid, IPC_RMID, 0);
    exit(0);
}